﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web.Script.Serialization;
using System.IO;


namespace NConf.ConfigSources
{
	public class JSONConfigSource : DictConfigSource
	{
		//---------------------------------------------------------------------

		#region Internal Members

		private string filename;

		private JavaScriptSerializer json;

		private Regex commentsRegex = new Regex(
				@"(^|\n)
					(?<keep>                         # Don't process comments inside strings ('\u0022' == double quotes)
						[^\u0022\n]*?                #    zero or more non-double-quote characters)
						((\u0022[^\u0022\n]*){2})*?  #    a string followed by zero or more non-double-quote characters
					)
					(
						//[^\n]*($|(?=\n))           # Single-line comment (ends before a newline, or at the end of the input)
						|                            # Multi-line comment:
							/\*                      #    start of comment
							[^*]*                    #    zero or more non-'*' characters
							(\*(?!/)                 #    a '*' followed by a zero-width match for any character other than '*' or '/'
								[^*]*                #    zero or more non-'*' characters
							)*
							\*/                      #    end of comment
					)
					",

				// Use 'single-line' mode so '.' matches '\n'
				RegexOptions.IgnorePatternWhitespace | RegexOptions.ExplicitCapture | RegexOptions.Singleline
				);

		/// <summary>
		/// A Read/Write thread lock
		/// </summary>
		private ReaderWriterLockSlim confLock = new ReaderWriterLockSlim();

		#endregion

		//---------------------------------------------------------------------

		#region Constructors

		/// <summary>
		/// Default Constructor.
		/// </summary>
		public JSONConfigSource (string filename) : base()
		{
			// Store the filename (for future save support)
			this.filename = filename;

			// Create our serializer
			json = new JavaScriptSerializer();

			// Parse the config file
			ParseJsonConfig();
		} // end JSONConfigSource

		#endregion

		//---------------------------------------------------------------------

		#region Internal Helpers

		private void ParseJsonConfig()
		{
			if(File.Exists(filename))
			{
				var contents = File.ReadAllText(filename);
				contents = StripComments(contents);
				Configuration = (Dictionary<string, object>) json.DeserializeObject(contents);
			} // end if
		} // end ParseJsonConfig

		private string StripComments(string input)
		{
			return commentsRegex.Replace(input, "${keep}");
		} // end StripComments

		#endregion

		//---------------------------------------------------------------------
	} // end JSONConfigSource
} // end NConf.ConfigSources